/**
	This file is part of Fighting Field.

	Fighting Field is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Fighting Field is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Fighting Field.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "StateMachine.h"

#include "State.h"

namespace FF
{
	StateMachine::StateMachine()
		: mCurrentState(NULL)
	{
		mStates.reserve(8);
	}

	StateMachine::~StateMachine()
	{
		ExecuteAllActions();
		PopAllStatesNow();
	}

	bool StateMachine::Step(const Ogre::Real& dt)
	{
		ExecuteAllActions();

		if (mCurrentState)
			mCurrentState->Step(dt);

		return true;
	}

	bool StateMachine::Update(const Ogre::Real& dt)
	{
		if (mCurrentState)
			mCurrentState->Update(dt);

		return true;
	}

	State* StateMachine::GetState(unsigned char level) const
	{
		if (level >= mStates.size())
			return NULL;
		else 
			return *(mStates.rbegin() + level);
	}

	void StateMachine::PopState(const StateParam& param /*= BLANK_STATE_PARAM*/)
	{
		mActions.push_back(std::pair<Action, std::pair<State*, StateParam> >(Pop, std::pair<State*, StateParam>((State*)NULL, param)));
	}

	void StateMachine::PopAllStates(const StateParam& param /*= BLANK_STATE_PARAM*/)
	{
		mActions.push_back(std::pair<Action, std::pair<State*, StateParam> >(PopAll, std::pair<State*, StateParam>((State*)NULL, param)));
	}

	void StateMachine::PopStateNow(const StateParam& param /*= BLANK_STATE_PARAM*/)
	{
		ExecuteAction(Pop, NULL, param);
	}

	void StateMachine::PopAllStatesNow(const StateParam& param /*= BLANK_STATE_PARAM*/)
	{
		ExecuteAction(PopAll, NULL, param);
	}

	void StateMachine::ExecuteAllActions()
	{
		while (!mActions.empty())
		{
			std::vector< std::pair<Action, std::pair<State*, StateParam> > >::iterator itr = mActions.begin();
			ExecuteAction(itr->first, itr->second.first, itr->second.second);
			mActions.erase(itr);
		}
	}

	bool StateMachine::ExecuteAction(Action action, State* state, const StateParam& param)
	{
		switch (action)
		{
		case Push:
			{
				State* previousState = mCurrentState;
				mCurrentState = state;

				if (!mCurrentState->Init(param))
				{
					DELETE(mCurrentState);
					mCurrentState = previousState;
					return false;
				}

				if (previousState)
					previousState->Exit(mCurrentState, param);

				mStates.push_back(mCurrentState);
				mCurrentState->Enter(previousState, param);
			}
			break;
		case Pop:
			if (mCurrentState)
			{
				State* previousState = mCurrentState;
				mCurrentState = NULL;

				mStates.pop_back();

				if (!mStates.empty())
					mCurrentState = mStates.back();

				previousState->Exit(mCurrentState, param);

				if(mCurrentState)
					mCurrentState->Enter(previousState, param);

				previousState->Release(param);
				DELETE(previousState);
			}
			break;
		case PopAll:
			while (GetState())
			{
				PopStateNow(param);
			}
			break;
		case Change:
			{
				State* previousState = mCurrentState;
				mCurrentState = state;

				if (!mCurrentState->Init(param))
				{
					DELETE(mCurrentState);
					mCurrentState = previousState;
					return false;
				}

				if(previousState)
				{
					previousState->Exit(mCurrentState, param);
					mStates.pop_back();
				}

				mStates.push_back(mCurrentState);
				mCurrentState->Enter(previousState, param);

				if (previousState)
				{
					previousState->Release(param);
					DELETE(previousState);
				}
			}
			break;
		}
		return true;
	}
}